/* shttpd - connecting to the internet
   Copyright (C) 2018 Ariadne Devos

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "fd.h"

#include <sHT/compiler.h>

#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>

static const int sHT_true = 1;

static int
sHT_enable_sockopt(int fd, int layer, int flag)
{
	return setsockopt(fd, layer, flag, &sHT_true, sizeof(sHT_true));
}

int
sHT_passive_ipv6(const struct sHT_passive_in6 *in)
{
	int fd = socket(AF_INET6, in->type, 0);
	if (sHT_unlikely(sHT_test_hidden(fd, fd < 0)))
		return -errno;
	int ret = sHT_enable_sockopt(fd, IPPROTO_IP, IP_FREEBIND);
	if (sHT_unlikely(sHT_test_hidden(ret, ret)))
		goto socket;
	ret = sHT_enable_sockopt(fd, SOL_SOCKET, SO_REUSEPORT);
	if (sHT_unlikely(sHT_test_hidden(ret, ret)))
		goto socket;
	/* XXX SO_ATTACH_BPF (seccomp filtering) */
	ret = bind(fd, (struct sockaddr *) &in->addr, sizeof(in->addr));
	if (sHT_unlikely(sHT_test_hidden(ret, ret < 0)))
		goto socket;
	ret = listen(fd, in->backlog);
	if (sHT_unlikely(sHT_test_hidden(ret, ret < 0)))
		goto socket;
	return fd;

socket:
	ret = -errno;
	sHT_close(fd);
	return ret;
}
